package com.corgi.multi.anno.config;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import cn.hutool.core.util.ObjectUtil;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;

/**
   * @类名：         <p>DynamicDataSourceRegister</p>
   * @功能描述： <p>把数据源注册到springboot</p>
   * @作者信息： <p>dengmiao</p>
   * @创建时间： <p>2018年5月6日下午1:02:35</p>
   * @修改备注：
 */
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {

	/**
	 * 默认数据源
	 * 1.x org.apache.tomcat.jdbc.pool.DataSource
	 * 2.x com.zaxxer.hikari.HikariDataSource
	 */
	private static String datasource_default = "com.zaxxer.hikari.HikariDataSource";
	
	private DataSource defaultDataSource;
	
	private PropertyValues dataSourcePropertyValues;
	
	private Map<String, DataSource> customDataSource = new HashMap<>();
	
	@Override
	public void setEnvironment(Environment arg0) {
		defaultDataSource(arg0);
		customDataSource(arg0);
	}
	
	/**
	 * 默认数据源解析
	 * @param arg0
	 */
	private void defaultDataSource(Environment arg0){
		// 1.x
		/*RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(arg0, "spring.datasource.");*/

		// 2.x
		Iterable<ConfigurationPropertySource> sources = ConfigurationPropertySources.get(arg0);
		Binder binder = new Binder(sources);
		BindResult<Properties> bindResult = binder.bind("spring.datasource", Properties.class);
		Properties resolver= bindResult.get();

		Map<String, Object> dsMap = new HashMap<String, Object>();
		dsMap.put("type", resolver.getProperty("type"));
		dsMap.put("driver-class-name", resolver.getProperty("driver-class-name"));
		dsMap.put("url", resolver.getProperty("url"));
		dsMap.put("username", resolver.getProperty("username"));
		dsMap.put("password", resolver.getProperty("password"));
		
		defaultDataSource = buildDataSource(dsMap);
		
		dataBind(defaultDataSource, arg0);
	}
	
	private void dataBind(DataSource dataSource, Environment environment){
		/*RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
		
		RelaxedDataBinder binder = new RelaxedDataBinder(dataSource);
		binder.setConversionService(new DefaultConversionService());
		binder.setIgnoreNestedProperties(false);
		binder.setIgnoreInvalidFields(false);
		binder.setIgnoreUnknownFields(true);*/
		
		if(dataSourcePropertyValues == null){
			/*Map<String, Object> subProperties =  new RelaxedPropertyResolver(environment,"spring.datasource").getSubProperties(".");*/

			Iterable<ConfigurationPropertySource> sources = ConfigurationPropertySources.get(environment);
			Binder binder = new Binder(sources);
			Properties properties = binder.bind("spring.datasource", Properties.class).get();
			Map<String, Object> subProperties = (Map) properties;

			Map<String, Object> values = new HashMap<>(subProperties);
			
			values.remove("type");
			values.remove("driver-class-name");
			values.remove("url");
			values.remove("username");
			values.remove("password");
			
			dataSourcePropertyValues = new MutablePropertyValues(values);
		}

		Iterable<ConfigurationPropertySource> sources = ConfigurationPropertySources.get(environment);
		Binder binder = new Binder(sources);
		binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(dataSource));

		/*dataBinder.bind(dataSourcePropertyValues);*/
		
	}
	
	private DataSource buildDataSource(Map<String, Object> dsMap){
		try {
			Object type = dsMap.get("type");
			if(type == null) 
				type = datasource_default;
			@SuppressWarnings("unchecked")
			Class<? extends DataSource> dataSource = (Class<? extends DataSource>) Class.forName((String) type);
			String driverClassName = dsMap.get("driver-class-name").toString();
			String url = dsMap.get("url").toString();
			String username = dsMap.get("username").toString();
			String password = dsMap.get("password").toString();
			//把数据源的相关信息绑定到springboot中
			DataSourceBuilder factory = DataSourceBuilder.create()
										.driverClassName(driverClassName)
										.url(url)
										.username(username)
										.password(password)
										.type(dataSource);
			return factory.build();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	@SuppressWarnings("unused")
	private void customDataSource(Environment arg0){
		/*RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(arg0,"cluster.datasource.");*/

		Iterable<ConfigurationPropertySource> sources = ConfigurationPropertySources.get(arg0);
		Binder binder = new Binder(sources);
		BindResult<Properties> bindResult = binder.bind("slave.datasource", Properties.class);
		Properties resolver= bindResult.get();

		String dsNames = resolver.getProperty("names");
		for (String dsName : dsNames.split(",")) {
			/*Map<String, Object> subProperties = resolver.getSubProperties(dsName + ".");*/
			Properties subProperties = binder.bind("slave.datasource." + dsName, Properties.class).get();
			Object weighto = subProperties.get("weight");
			Integer weight = 1;
			if(weighto != null && ObjectUtil.isValidIfNumber(weighto.toString())){
				weight = Integer.parseInt(weight.toString());
			}
			DynamicDataSourceContextHolder.getWeightMap().put(dsName, weight);
			DataSource dataSource = buildDataSource(new HashMap<String, Object>((Map) subProperties));
			customDataSource.put(dsName, dataSource);
			dataBind(dataSource, arg0);
		}
	}

	/**
	 * 把数据库连接池实例化
	 */
	@Override
	public void registerBeanDefinitions(AnnotationMetadata arg0,
			BeanDefinitionRegistry arg1) {
		Map<Object, Object> targetDataSource = new HashMap<Object, Object>();
		targetDataSource.put("write", defaultDataSource);
		DynamicDataSourceContextHolder.getDataSourceIds().add("write");
		targetDataSource.putAll(customDataSource);
		for(String key : customDataSource.keySet()){
			DynamicDataSourceContextHolder.getDataSourceIds().add(key);
		}
		
		GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
		beanDefinition.setBeanClass(DynamicDataSource.class);
		beanDefinition.setSynthetic(true);
		MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
		propertyValues.addPropertyValue("defaultTargetDataSource", defaultDataSource);
		propertyValues.addPropertyValue("targetDataSources", targetDataSource);
		arg1.registerBeanDefinition("dataSource", beanDefinition);
	}

}
